package battle_system

import (
	"confs/gen/common"
	"game/app/actors"
	"game/app/actors/schemas"
	"game/app/systems/map_system/map_data_system"
	"game/app/systems/map_system/mission_helper"
	"game/app/systems/math_system/math_utils"
	"game/app/systems/move_system"
	"game/app/systems/move_system/move_defines"
	"gos/bases/parse"
	"gos/bases/time_tools"
	"gos/modules/logger"
	"math"
)

func init() {
	actors.RegisterBattleTicker("battle_ai_update", BattleAI.TickerBattleAIRefresh)
}

// BattleSystem 战斗系统
type BattleAISystem struct{}

type IBattleAISystem interface {
	TickerBattleAIRefresh(state *schemas.MoveState) error
	CreateAI(state *schemas.MoveState, battleAIEntity *common.MoveEntity,
		msgCallback func(done bool, reason int32, value any)) *schemas.BattleAIEntity
	ChangeAIState(state *schemas.MoveState, entity *schemas.MoveEntity, battleAiEntity *schemas.BattleAIEntity, aiState common.BattleEntityAIState)
}

var BattleAI IBattleAISystem = BattleAISystem{}

// AttackAI
func (s BattleAISystem) AttackAI(state *schemas.MoveState, battleAIEntity *schemas.BattleAIEntity) {
	entity := state.GetMoving(battleAIEntity.Id)
	if entity == nil {
		logger.ERR("Ai攻击错误, 没有找到实体:", battleAIEntity.Id)
		return
	}

	targetEntity := state.GetMoving(battleAIEntity.TargetId)
	if targetEntity == nil {
		logger.ERR("Ai攻击错误, 没有找到目标实体:", battleAIEntity.TargetId)
		return
	}

	if entity.FollowTargetId <= 0 {
		logger.ERR("Ai攻击错误, 没有目标追击信息:", entity, " ", targetEntity)
		return
	}

	noticeDist := math_utils.MakeZNumberByInt32(1)
	minDist := entity.Collision.Add(targetEntity.Collision)
	minDist = minDist.Add(noticeDist)
	minDistSquare := minDist.Mul(minDist)
	inRange := entity.CurPos.Sub(targetEntity.CurPos).LengthSquare().Lq(minDistSquare)
	// 在攻击范围内，创建战斗
	if inRange {
		if !Battles.HasBattle(state, entity.Id, targetEntity.Id) {
			// err := BattleBehavior.BattleCreateNotice(state, state.SceneId, entity.Id, targetEntity.Id)
			// if err != nil {
			// 	logger.ERR("追击成功,在攻击范围内 失败:", err)
			// 	return
			// }
			// logger.ERR("追击成功,在攻击范围内，开始追击回调创建战斗:", entity.Id, " ", targetEntity.Id)
		}
	} else {
		// 如果占用了位置，但是又超过了射程，需要移除我占用的位置
		_, ok := targetEntity.GetSlot(entity.Id)
		if ok {
			targetEntity.RemoveSlot(entity.Id)
		}
	}

	// 互相攻击的情况下, 进入射程后，必须有一方停下来
	if targetEntity.FollowTargetId == entity.Id {
		// 没有进入射程，不需要特殊处理
		for {
			if !inRange {
				break
			}

			// 被夹击人少的，不需要停止，被夹击人数多的才需要先停止
			if len(entity.Slots) < len(targetEntity.Slots) {
				break
			}

			// 夹击人数相同，那么判断id小的一方停下来
			if len(entity.Slots) == len(targetEntity.Slots) {
				if targetEntity.Id < entity.Id {
					break
				}
			}

			// 如果对手没有移动，可以开始走动了
			if !s.IsMoving(state, targetEntity) {
				s.MoveRound(state, entity, targetEntity, battleAIEntity)
				return
			}

			// 如果自己没有移动，那就什么都不做
			if move_system.Move.IsMoveNone(state, entity) {
				s.SetStatus(state, common.BattleEntityAIStatus_AIStatusNone, entity, battleAIEntity)
				return
			} else {
				move_system.Move.StopMove(state, entity, move_defines.StopMoveReasonMarchAI)
				s.SetStatus(state, common.BattleEntityAIStatus_AIStatusNone, entity, battleAIEntity)
				return
			}
		}
	}

	isTargetMoving := s.IsMoving(state, targetEntity)
	if battleAIEntity.Status == common.BattleEntityAIStatus_AIStatusStickAttack {
		if isTargetMoving {
			if s.IsCanJump(state, entity, targetEntity) {
				s.JumpToRadius(state, entity, targetEntity)
				return
			}

			s.FollowAttack(state, 1, entity, targetEntity, battleAIEntity, nil)
			return
		}

		s.JumpToRadius(state, entity, targetEntity)
		s.MoveRound(state, entity, targetEntity, battleAIEntity)
		return
	}

	if battleAIEntity.Status == common.BattleEntityAIStatus_AIStatusMovePos || battleAIEntity.Status == common.BattleEntityAIStatus_AIStatusNone {
		if inRange && !isTargetMoving {
			s.MoveRound(state, entity, targetEntity, battleAIEntity)
		} else {
			s.FollowAttack(state, 2, entity, targetEntity, battleAIEntity, nil)
		}
		return
	}

	if battleAIEntity.Status == common.BattleEntityAIStatus_AIStatusFollowAttack {
		if inRange && !isTargetMoving {
			// s.MoveRound(state, entity, targetEntity, battleAIEntity)
		} else {
			if move_system.Move.IsMoveNone(state, entity) {
				s.FollowAttack(state, 3, entity, targetEntity, battleAIEntity, nil)
			}
		}
	}
}

// TickerBattleAIRefresh
func (s BattleAISystem) TickerBattleAIRefresh(state *schemas.MoveState) error {
	state.AiBattleEntities.Range(func(k, v interface{}) bool {
		battleAIEntity := v.(*schemas.BattleAIEntity)
		if battleAIEntity.AIState == common.BattleEntityAIState_AIStateAttack || battleAIEntity.AIState == common.BattleEntityAIState_AIStateRangeAattack {
			s.AttackAI(state, battleAIEntity)
		}
		return true
	})
	return nil
}

// SetStatus
func (s BattleAISystem) SetStatus(state *schemas.MoveState, aiStatus common.BattleEntityAIStatus, entity *schemas.MoveEntity, battleAIEntity *schemas.BattleAIEntity) {
	if aiStatus == battleAIEntity.Status {
		return
	}

	srcStatus := battleAIEntity.Status
	battleAIEntity.Status = aiStatus
	if srcStatus == common.BattleEntityAIStatus_AIStatusStickAttack || aiStatus == common.BattleEntityAIStatus_AIStatusStickAttack {
		BattleBehavior.BattleStickNotice(state, state.SceneId, battleAIEntity.Id, battleAIEntity.TargetId, battleAIEntity.IsStick(),
			&common.MoveData{
				CurPos:       entity.CurPos.ToVector2D(),
				Direction:    entity.Direction.ToVector2D(),
				UpdateMoveAt: entity.PreCheckTime,
			})
	}
}

// ChangeAIState
func (s BattleAISystem) ChangeAIState(state *schemas.MoveState, entity *schemas.MoveEntity, battleAiEntity *schemas.BattleAIEntity, aiState common.BattleEntityAIState) {
	battleAiEntity.AIState = aiState
	s.SetStatus(state, common.BattleEntityAIStatus_AIStatusNone, entity, battleAiEntity)
}

// CalNearSlot 计算占位
func (s BattleAISystem) CalNearSlot(state *schemas.MoveState, angle float64) int32 {
	slot := int32(angle / move_defines.SlotUnit)
	slotCur := float64(slot) * move_defines.SlotUnit
	slotNext := float64(slot+1) * move_defines.SlotUnit
	angleCur := math.Abs(slotCur - angle)
	angleNext := math.Abs(slotNext - angle)

	if angleCur > math.Pi {
		angleCur = 2*math.Pi - angleCur
	}

	if angleNext > math.Pi {
		angleNext = 2*math.Pi - angleNext
	}

	if angleCur > angleNext {
		slot = slot + 1
	}

	if slot >= move_defines.SlotNum {
		slot = 0
	}
	return slot
}

// GetSlotPos
func (s BattleAISystem) GetSlotPos(state *schemas.MoveState, entity *schemas.MoveEntity, targetEntity *schemas.MoveEntity, slot int32) math_utils.Vector2D {
	big := false
	if slot >= move_defines.SlotNum {
		big = true
		slot = slot % move_defines.SlotNum
	}

	angle := float64(slot)*move_defines.SlotUnit - math.Pi
	if big {
		angle += move_defines.SlotUnit / 2
	}

	// + (math_utils.MakeByReal(1).ToFloat64() / 2)
	dis := entity.Collision.ToFloat64() + targetEntity.Collision.ToFloat64()
	if dis < 0 {
		dis = 0
	}

	x := math.Cos(angle)*dis + targetEntity.CurPos.X.ToFloat64()
	y := math.Sin(angle)*dis + targetEntity.CurPos.Y.ToFloat64()
	newPos := math_utils.MakeVecto2dByFloat64(x, y)
	return newPos
}

// FindSlot
func (s BattleAISystem) FindSlot(state *schemas.MoveState, slot *int32, findSlot *bool, slotPos *math_utils.Vector2D, angle float64,
	entity *schemas.MoveEntity, targetEntity *schemas.MoveEntity, index int32, empty bool) {
	minSlot := 2 * math.Pi
	*findSlot = false
	for i := 0; i < move_defines.SlotNum; i++ {
		slotIndex := float64(i) * move_defines.SlotUnit
		if index > 0 {
			slotIndex += move_defines.SlotUnit / 2
		}

		angleIndex := math.Abs(slotIndex - angle)
		if angleIndex > math.Pi {
			angleIndex = 2*math.Pi - angleIndex
		}

		findSlotIndex := int32(i) + index
		*slotPos = s.GetSlotPos(state, entity, targetEntity, findSlotIndex)
		if (!empty || targetEntity.SlotEmpty(findSlotIndex)) && (angleIndex < minSlot && move_system.Move.CheckMoveTo(state, *slotPos)) {
			*slot = findSlotIndex
			minSlot = angleIndex
			*findSlot = true
		}
	}

	if !*findSlot && index > 0 {
		for i := 0; i < move_defines.SlotNum; i++ {
			slotIndex := float64(i) * move_defines.SlotUnit
			if index > 0 {
				slotIndex += move_defines.SlotUnit / 2
			}

			angleIndex := math.Abs(slotIndex - angle)
			if angleIndex > math.Pi {
				angleIndex = 2*math.Pi - angleIndex
			}

			findSlotIndex := int32(i) + index
			*slotPos = s.GetSlotPos(state, entity, targetEntity, findSlotIndex)

			if angleIndex < minSlot && move_system.Move.CheckMoveTo(state, *slotPos) {
				*slot = findSlotIndex
				minSlot = angleIndex
				*findSlot = true
			}
		}
	}
}

// FindSlotWrap
func (s BattleAISystem) FindSlotWrap(state *schemas.MoveState, slot *int32, findSlot *bool, slotPos *math_utils.Vector2D, angle float64,
	entity *schemas.MoveEntity, targetEntity *schemas.MoveEntity, empty bool) {
	s.FindSlot(state, slot, findSlot, slotPos, angle, entity, targetEntity, 0, empty)
	if !*findSlot {
		s.FindSlot(state, slot, findSlot, slotPos, angle, entity, targetEntity, move_defines.SlotNum, empty)
	}
}

// MoveRound
func (s BattleAISystem) MoveRound(state *schemas.MoveState, entity *schemas.MoveEntity, targetEntity *schemas.MoveEntity, aiEntity *schemas.BattleAIEntity) {
	_, ok := targetEntity.GetSlot(entity.Id)
	if ok {
		targetEntity.RemoveSlot(entity.Id)
	}

	dir := entity.CurPos.Sub(targetEntity.CurPos)
	angle := math.Atan2(dir.Y.ToFloat64(), dir.X.ToFloat64()) + math.Pi
	slot := s.CalNearSlot(state, angle)
	slotPos := s.GetSlotPos(state, entity, targetEntity, slot)
	findSlot := false
	// 如果我攻击对方，对方也攻击我，那么自己占用最近的点位
	if targetEntity.FollowTargetId == entity.Id {
		targetEntity.RemoveSlotByIndex(slot)
		if !move_system.Move.CheckMoveTo(state, slotPos) {
			// 从新寻找一个位置
			s.FindSlotWrap(state, &slot, &findSlot, &slotPos, angle, entity, targetEntity, true)
		} else {
			findSlot = true
		}
	} else {
		if !targetEntity.SlotEmpty(slot) || !move_system.Move.CheckMoveTo(state, slotPos) {
			s.FindSlotWrap(state, &slot, &findSlot, &slotPos, angle, entity, targetEntity, true)
		} else {
			findSlot = true
		}
	}

	if findSlot {
		targetEntity.AddSlot(entity.Id, slot)
		s.MoveToSlot(state, entity, targetEntity, aiEntity, slot)
	} else {
		logger.ERR("无法找到合理点位:", entity, " ", targetEntity)
	}
}

// MoveToSlot 移动到某个点位
func (s BattleAISystem) MoveToSlot(state *schemas.MoveState, entity *schemas.MoveEntity, targetEntity *schemas.MoveEntity, battleAIEntity *schemas.BattleAIEntity, slot int32) {
	newPos := s.GetSlotPos(state, entity, targetEntity, slot)
	ignoreDis := math_utils.MakeZNumberByFloat64(0.002 * 0.002)
	if newPos.Sub(battleAIEntity.LastMovePos).LengthSquare().Me(ignoreDis) && battleAIEntity.IsFirstMove {
		// if battleAIEntity.IsFirstMove {
		logger.ERR("处于攻击范围内,开始向合理点位移动: CurPos:", entity.CurPos, " NewPos:", newPos, " TargetCurPos:", targetEntity.CurPos, " Slot:", slot)
		move_system.Move.MoveTo(state, entity, newPos.ToVector2D(), nil, func(done bool, reason int32) bool {
			logger.WARN("移动某个点位完成:", entity.Id, " reason:", reason)
			if reason == move_defines.StopMoveReasonMarchAI {
				return false
			}

			if !done {
				state.DeleteBattleAIEntityWrap(entity)
				if reason == move_defines.StopMoveReasonPathFindError ||
					reason == move_defines.StopMoveReasonZeroSpeed || reason == move_defines.StopMoveReasonUnknown {
					logger.ERR("移动某个点位错误:", entity.Id)
				}
			}
			return false
		})

		battleAIEntity.LastMovePos = newPos
		battleAIEntity.IsFirstMove = false
		s.SetStatus(state, common.BattleEntityAIStatus_AIStatusMovePos, entity, battleAIEntity)
	}
}

// FollowAttack
func (s BattleAISystem) FollowAttack(state *schemas.MoveState, reason int, entity *schemas.MoveEntity, targetEntity *schemas.MoveEntity,
	battleAIEntity *schemas.BattleAIEntity, msgCallback func(done bool, reason int32, value any)) bool {
	// 开始追击流程
	logger.WARN("开始追击:", entity.Id, " ", targetEntity.Id, " ", reason)
	battleAIEntity.IsFirstMove = true
	err := move_system.Move.MoveFollow(state, entity, targetEntity, msgCallback, func(done bool, reason int32) bool {
		if reason == move_defines.StopMoveReasonMarchAI {
			return false
		}

		if !done {
			state.DeleteBattleAIEntityWrap(entity)
			if reason == move_defines.StopMoveReasonPathFindError ||
				reason == move_defines.StopMoveReasonZeroSpeed || reason == move_defines.StopMoveReasonUnknown {
				logger.ERR("移动追击失败:", reason, " ", battleAIEntity)
			}
			return true
		}

		line, ok := map_data_system.MapMarchLine.RpcGetMarchLine(parse.ToInt64(state.SceneId), entity.Id)
		if !ok {
			logger.ERR("追击没有找到A的行军线信息:", entity.Id)
			return true
		}

		targetLine, ok := map_data_system.MapMarchLine.RpcGetMarchLine(parse.ToInt64(state.SceneId), targetEntity.Id)
		if !ok {
			logger.ERR("追击没有找到B的行军线信息:", targetEntity.Id)
			return true
		}

		cdvls := entity.CurPos.Sub(targetEntity.CurPos).LengthSquare()
		noticeDist := math_utils.MakeZNumberByInt32(1)
		minDist := entity.Collision.Add(targetEntity.Collision)
		minDist = minDist.Add(noticeDist)
		minDistSquare := minDist.Mul(minDist)
		logger.ERR("2 cdvls:", cdvls, " ", minDistSquare, " ", entity.CurPos, " ", targetEntity.CurPos)
		// 在攻击范围内，创建战斗
		if cdvls.Lq(minDistSquare) && reason == move_defines.StopMoveReasonSuccess {
			if !Battles.HasBattle(state, entity.Id, targetEntity.Id) {
				attackSoldiers := mission_helper.Helper.GetBattleSoldierByLine(line)
				defenceSoldiers := mission_helper.Helper.GetBattleSoldierByLine(targetLine)
				ok, battle := Battles.CreateBattle(state, attackSoldiers, defenceSoldiers, common.BattleCreateReason_AttackAI)
				if !ok {
					logger.ERR("创建战斗失败:", attackSoldiers, defenceSoldiers)
					return false
				}

				BattleBehavior.BattleCreateNotice(state, state.SceneId, battle.BattleId, attackSoldiers, defenceSoldiers, &common.BattleInfo{
					InitSoldiers:   battle.A.TroopCom.InitSoldiers,
					RemainSoldiers: battle.A.TroopCom.TotalSoldiers,
					Relation: &common.BattleRelation{
						Id:   battle.A.Id,
						Type: battle.A.Type,
					},
					Direction: battle.A.Position.Sub(battle.B.Position).ToVector2D(),
				}, &common.BattleInfo{
					InitSoldiers:   battle.B.TroopCom.InitSoldiers,
					RemainSoldiers: battle.B.TroopCom.TotalSoldiers,
					Relation: &common.BattleRelation{
						Id:   battle.B.Id,
						Type: battle.B.Type,
					},
					Direction: battle.B.Position.Sub(battle.A.Position).ToVector2D(),
				})
				logger.WARN("追击成功,在攻击范围内，开始追击回调创建战斗:", entity.Id, " ", targetEntity.Id)
			}
		}

		// 检测攻击目标是否可移动
		if s.IsMoving(state, targetEntity) {
			// 如果我的速度大于等于对方，并且不是互相攻击，就切换为粘着状态，
			if s.IsCanJump(state, entity, targetEntity) {
				logger.WARN("进入到沾着状态，开始跳跃,", entity.Id, " ", targetEntity.Id)
				s.SetStatus(state, common.BattleEntityAIStatus_AIStatusStickAttack, entity, battleAIEntity)
				move_system.Move.StopMove(state, entity, move_defines.StopMoveReasonMarchAI)
			}
		} else {
			s.MoveRound(state, entity, targetEntity, battleAIEntity)
		}
		return true
	})

	if err != nil {
		logger.ERR("CreateAI 3")
		return false
	}

	s.SetStatus(state, common.BattleEntityAIStatus_AIStatusFollowAttack, entity, battleAIEntity)
	return true
}

// CreateAI 创建AI流程
func (s BattleAISystem) CreateAI(state *schemas.MoveState, moveEntity *common.MoveEntity,
	msgCallback func(done bool, reason int32, value any)) *schemas.BattleAIEntity {
	// 创建AI实体
	battleAIEntity := state.GetBattleAIEntity(moveEntity.Id)
	if battleAIEntity == nil {
		battleAIEntity = schemas.NewBattleAIEntity(moveEntity.Id, moveEntity.TargetId)
	} else {
		if battleAIEntity.Status == common.BattleEntityAIStatus_AIStatusStickAttack && battleAIEntity.TargetId == moveEntity.TargetId {
			return battleAIEntity
		}
		battleAIEntity.Update(moveEntity.Id, moveEntity.TargetId)
	}

	// 如果目标存在，更新坐标
	targetEntity := move_system.Move.FindAndUpdate(state, moveEntity.TargetId)
	if targetEntity == nil {
		logger.ERR("CreateAI 1", moveEntity.TargetId)
		return nil
	}

	// 创建追击移动实体
	entity := move_system.Move.CreateEntity(state, &common.MoveEntity{
		Id:        moveEntity.Id,
		Type:      moveEntity.Type,
		Speed:     moveEntity.Speed,
		CurPos:    moveEntity.CurPos,
		Direction: moveEntity.Direction,
		Category:  moveEntity.Category,
		Collision: moveEntity.Collision,
		TargetPos: targetEntity.CurPos.ToVector2D(),
	})

	s.ChangeAIState(state, entity, battleAIEntity, common.BattleEntityAIState_AIStateAttack)
	if !s.FollowAttack(state, 0, entity, targetEntity, battleAIEntity, msgCallback) {
		logger.ERR("CreateAI 2")
		return nil
	}
	return battleAIEntity
}

// IsMoving 是否移动中
func (s BattleAISystem) IsMoving(state *schemas.MoveState, targetEntity *schemas.MoveEntity) bool {
	battleAIEntity := state.GetBattleAIEntity(targetEntity.Id)
	if battleAIEntity == nil {
		return false
	}

	if !move_system.Move.IsMoveNone(state, targetEntity) {
		return true
	}
	return battleAIEntity.IsStick()
}

// IsCanJump 是否可以跳跃
func (s BattleAISystem) IsCanJump(state *schemas.MoveState, entity *schemas.MoveEntity, targetEntity *schemas.MoveEntity) bool {
	battleAIEntity := state.GetBattleAIEntity(targetEntity.Id)
	if battleAIEntity == nil {
		return false
	}

	if entity.MoveSpeed.Le(targetEntity.MoveSpeed) {
		return false
	}

	if battleAIEntity.IsStick() && targetEntity.FollowTargetId == entity.Id {
		return false
	}
	return true
}

// JumpToRadius
func (s BattleAISystem) JumpToRadius(state *schemas.MoveState, entity *schemas.MoveEntity, targetEntity *schemas.MoveEntity) {
	outPos := move_system.Move.CalcFollowPos(state, entity, targetEntity)
	if outPos == nil {
		logger.ERR("跳跃错误")
		return
	}

	jumpPos := math_utils.MakeVector2dByProto(outPos)
	if !move_system.Move.CheckMoveTo(state, jumpPos) {
		var slot int32
		findSlot := false
		dir := entity.CurPos.Sub(targetEntity.CurPos)
		angle := math.Atan2(dir.Y.ToFloat64(), dir.X.ToFloat64()) + math.Pi
		s.FindSlotWrap(state, &slot, &findSlot, &jumpPos, angle, entity, targetEntity, false)
		if !findSlot {
			logger.ERR("跳跃错误,查找点位失败:", entity, " ", targetEntity)
			return
		}
	}

	logger.WARN("粘着状态广播同步:", entity.Id, " ", targetEntity.Id)
	entity.CurPos = jumpPos
	entity.PreCheckTime = time_tools.Now().Unix()
	entity.Direction = targetEntity.TargetPos.Sub(entity.CurPos)
	BattleBehavior.BattleStickJumpNotice(state, state.SceneId, entity.Id, targetEntity.Id, &common.MoveData{
		CurPos:       entity.CurPos.ToVector2D(),
		Direction:    entity.Direction.ToVector2D(),
		UpdateMoveAt: entity.PreCheckTime,
	})
}
